﻿using System.Collections.Generic;
using System.Linq;
using NRank.Algorithms.Strategies;
using NRank.Common;

namespace NRank.Benchmark.Queries
{
	/// <summary>
	/// Implements benchmark query: One join on large table (Hotels join Rooms)
	/// </summary>
	public class JoinOnLargeRight : BenchmarkQuerySet
	{
		protected override IEnumerable<IRanked<JoinResult>> HashRankJoinRoundRobinTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Hotels
				.OrderByDescending(h => h.Stars)
				.HashRankJoin(
					cx.Rooms.OrderByDescending(r => r.Rating),
					h => h.Stars,
					r => r.Rating,
					h => h.IdHotel,
					r => r.IdHotel, 
					(h, r) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating}, 
					PullingStrategyTypes.Alternating);
		}

		protected override IEnumerable<IRanked<JoinResult>> HashRankJoinAdaptiveTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Hotels
				.OrderByDescending(h => h.Stars)
				.HashRankJoin(
					cx.Rooms.OrderByDescending(r => r.Rating),
					h => h.Stars,
					r => r.Rating,
					h => h.IdHotel,
					r => r.IdHotel, 
					(h, r) => new JoinResult { IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating }, 
					PullingStrategyTypes.MinimizeThreshold);
		}

		protected override IEnumerable<IRanked<JoinResult>> RankJoinTestRoundRobin(NRankBenchmarkDbDataContext cx)
		{
			return cx.Hotels
				.OrderByDescending(h => h.Stars)
				.RankJoin(
					cx.Rooms.OrderByDescending(r => r.Rating),
					h => h.Stars,
					r => r.Rating,
					(h, r) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating},
					(h, r) => h.IdHotel == r.IdHotel,
					PullingStrategyTypes.Alternating
				);
		}

		protected override IEnumerable<IRanked<JoinResult>> RankJoinTestAdaptive(NRankBenchmarkDbDataContext cx)
		{
			return cx.Hotels
				.OrderByDescending(h => h.Stars)
				.RankJoin(
					cx.Rooms.OrderByDescending(r => r.Rating),
					h => h.Stars,
					r => r.Rating,
					(h, r) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating},
					(h, r) => h.IdHotel == r.IdHotel,
					PullingStrategyTypes.MinimizeThreshold);
		}

		protected override IEnumerable<IRanked<JoinResult>> NestedLoopJoinTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Rooms.OrderByDescending(r => r.Rating)
				.NestedLoopRankJoin(
					cx.Hotels,
					r => r.Rating,
					h => h.Stars,
					(r, h) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating},
					(r, h) => r.IdHotel == h.IdHotel);
		}

		protected override IEnumerable<IRanked<JoinResult>> NestedLoopJoinEqTest(NRankBenchmarkDbDataContext cx)
		{
			return cx.Rooms.OrderByDescending(r => r.Rating)
				.NestedLoopRankJoin(
					cx.Hotels,
					r => r.Rating,
					h => h.Stars,
					(r, h) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = h.Stars + r.Rating},
					r => r.IdHotel,
					h => h.IdHotel);
		}

		protected override IEnumerable<IRanked<JoinResult>> TraditionalQuery(NRankBenchmarkDbDataContext cx)
		{
			return cx.Rooms
				.Join(cx.Hotels,
				      r => r.IdHotel,
				      h => h.IdHotel,
				      (r, h) => new JoinResult {IdHotel = h.IdHotel, IdRoom = r.IdRoom, Rating = r.Rating + h.Stars})
				.OrderByDescending(j => j.Rating)
				//.Take(i)
				.AsRanked(r => r.Rating);
		}
	}
}